package com.sparrow.common.console.manager;

import com.google.common.base.Preconditions;
import com.google.common.collect.Maps;
import com.sparrow.common.console.annotation.Command;
import com.sparrow.common.console.domain.ConsoleCommandWrapper;
import com.sparrow.common.util.PropertiesUtil;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.core.convert.ConversionService;
import org.springframework.stereotype.Component;
import org.springframework.util.ReflectionUtils;

import javax.annotation.PostConstruct;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Map;

/**
 * 控制台命令
 */
@Component
public class ConsoleCommandManager implements  DisposableBean, BeanPostProcessor {

    private static Logger logger = LoggerFactory.getLogger(ConsoleCommandManager.class);

    private final Map<String, ConsoleCommandWrapper> wrapperMap = Maps.newHashMap();

    @Autowired
    private ConversionService conversionService;
    private Thread thread;

    @PostConstruct
    private void init() {
        start();
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        reg(bean);
        return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
    }

    private void reg(Object target) {
        ReflectionUtils.doWithMethods(target.getClass(), method -> {
            Command annotation = method.getAnnotation(Command.class);
            ConsoleCommandWrapper pre = wrapperMap.putIfAbsent(annotation.cmd(), new ConsoleCommandWrapper(target, method, conversionService));
            Preconditions.checkState(pre == null, annotation.cmd() + " 重复注册!");
        }, method -> method.isAnnotationPresent(Command.class));
    }

    public void start() {
        if (!PropertiesUtil.getPropertyOrDefault("console.command", "false").equals("true")) {
            return;
        }
        this.thread = new Thread(new ConsoleCommandRunner(), "ConsoleCommandRunner");
        thread.setDaemon(true);
        thread.start();
    }

    @Override
    public void destroy() throws Exception {
        if (this.thread != null) {
            this.thread.interrupt();
        }
    }

    private class ConsoleCommandRunner implements Runnable {

        @Override
        public void run() {
            BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
            try {
                while (true) {
                    final String line = in.readLine();
                    final String name = getName(line);
                    ConsoleCommandWrapper wrapper = wrapperMap.get(name);
                    if (wrapper == null) {
                        System.err.println("命令不存在：" + name);
                        continue;
                    }

                    final String[] args = getArguments(line);
                    try {
                        wrapper.execute(args);
                        logger.info("执行 cmd:{} over", name);
                    } catch (Exception e) {
                        logger.error("console command", e);
                    }
                }
            } catch (Exception e) {
                throw new RuntimeException("获取命令行输入时出现错误", e);
            }
        }

        String getName(String line) {
            if (StringUtils.isBlank(line)) {
                return null;
            }

            String[] arguments = StringUtils.split(line);
            if (arguments.length == 0) {
                return null;
            }
            return arguments[0];
        }

        String[] getArguments(String line) {
            if (StringUtils.isBlank(line)) {
                return new String[0];
            }
            String[] arguments = StringUtils.split(line);
            if (arguments.length <= 1) {
                return new String[0];
            }
            String[] result = new String[arguments.length - 1];
            System.arraycopy(arguments, 1, result, 0, arguments.length - 1);
            return result;
        }
    }


}
